#! /usr/bin/env ruby
# encoding: utf-8





# Copyright 2021 陈睿超 (Chen, Ruichao) <sheep0x.moe@protonmail.com>
# This work is free. You can redistribute it and/or modify it under the
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.



#
# Bracket Notation Normalizer for linguistics students
#
# version: 1b (2021 May 13)
#
# What it does:
#    Do you like using spaces and new lines while parsing a sentence on scratch paper,
#    but find it unwieldy for pasting in chatrooms and slides?  If you do, this tool
#    is for you.
#
# Example Input:
#    [a pineapple
#         [on     [a pen]
#         ][under  [an apple]]
#    ]
# Example Output:
#    [a pineapple [on [a pen]] [under [an apple]]]
#
# This script does NOT balance your brackets or otherwise validate them.
# All it does is adding/removing whitespaces.
#



#
# The source code is heavily commented in the hope of making it more accessible.
# I hope to demonstrate that, contrary to popular beliefs, modern programming is more or less hygienic.
# In particular, the reader is invited to count the parentheses and try to balance them.
#
# The same script without commentary is attached at the end.
# It is 17 lines long.
#
# Q: Isn't there a license without profanity?
# A: Yes. But there is no well-known concise license without profanity, except CC0.
#    I prefer a lighthearted paragraph to a scary declaration that overtly, fully, permanently,
#    irrevocably and unconditionally waives, abandons, and surrenders all of my rights.
#




def read_line_or_die
    (line = gets) or exit
    line.chomp           # remove line separators
end


puts "Reminder:"
puts "    Each sentence must be exactly 10 lines."
puts "    If it's less than 10 lines, just pad with empty lines."
puts "    If it's more than 10 lines, edit the source code to accomodate for more lines."
puts "    To quit, press Ctrl-D (Linux/macOS/online) or <Ctrl-Z> <Enter> (Windows)"
puts "    Alternatively, press Ctrl-C on any system.  This will print out some scary messages, but you can safely ignore them."


loop do
    puts
    puts "Enter a new sentence:"
    lines = 10.times.collect { read_line_or_die }

    # Explanation:
    #
    # 3 kinds of parts:  opening bracket, closing bracket, words (consisting of English letters)
    # 2 kinds of pseudo-parts:  beginning of sentence, end of sentence
    #
    # 1 space between      ] [
    # 1 space between      ] word
    # 1 space between   word [
    # 1 space between  word1 word2
    #
    # 0 spaces between     []
    # 0 spaces between     [[
    # 0 spaces between     [word
    # 0 spaces between        ]]
    # 0 spaces between     word]
    #
    # That accounts for all 3x3 = 9 cases
    result = lines.map {|l| l + ' '}.              # turns "word1" "word2" into "word1 " "word2 ", so that...
                   join.                           # ... when put together, they form "word1 word2 " instead of "word1word2"
                   strip.                          # remove leading and trailing spaces
                   squeeze(' ').                   # turns consecutive spaces into 1 space
                   gsub( /\[ /         ,  '[' ).   # remove spaces for [ x
                   gsub( / \]/         ,  ']' ).   # remove spaces for x ]
                   gsub( /\](?=\[|\w)/ ,  '] ').   # add space for ]x except ]]
                   gsub( /(?<=\w)\[/   ,  ' [')    # add space for word[
    puts result
end






#
### Stripped source code:
#
# def read_line_or_die
#    (line = gets) or exit
#    line.chomp
# end
#
# loop do
#    puts "\nEnter a new sentence:"
#    lines = 10.times.collect { read_line_or_die }
#
#    result = lines.map {|l| l + ' '}.
#                   join .strip .squeeze(' ').
#                   gsub( /\[ /          ,  '[' ).
#                   gsub( / \]/          ,  ']' ).
#                   gsub( /\](?=\[|\w)/  ,  '] ').
#                   gsub( /(?<=\w)\[/    ,  ' [')
#    puts result
# end
#




#
# Bonus question for those who made it this far:
#
# Q: What's that cryptic thing enclosed by two slashes?
#
# A: It is a regular expression, which is a concise way to describe a regular grammar.
#    Don't be scared by the terminology -- we've seen it in class!
#    Here is a side by side comparison between regex and (the right hand side of) PSR:
#
#         regex            PSR
#    ----------------------------------------
#          AB              A B                 A followed by B
#
#          A*               A*                 A repetead 0 or 1 or 2 or ... times
#
#         (A|B)          {  A                  either A or B
#                           B  }
#
#          (|A)            (A)                 A repeated 0 or 1 times (either nothing or A)
#
#    Here is how a computer scientist describes noun phrases:
#
#        / ( | Det)  AdjP*  N  PP* /
#
#    Looks familiar?
#
#    Ruby uses a heavily extended dialect of regex, which adds context awareness, among many other things.
#
#                   A (?= B )                  A followed by B, but B is not part of the phrase
#          (?<= B ) A                          A preceded by B, but B is not part of the phrase
#
#    Context sensitivity allows us to express stuffs like
#       "replace any [ following a word with a space and a [, but don't touch that word"
#
#    by saying
#       gsub( / (?<= \w ) \[ /,  " space [ " )
#
#    where gsub stands for globally substitute.
#
#    Acute readers might have noticed the peculiar use of the word "dialect".
#    Yep, regex is itself a language!  It is a metalanguage that describes other languages.
#    English is also a metalanguage.  When we explain a meme to our friends, we are using English to explain English!
#
#    And regex is a language embedded in another language called Ruby. Is regex a part of Ruby, or a separate language? Oh no, my brain hurts!
#
#    Let's stop this never ending digression here.
#
